package com.ccp.dev.workflow.service.bpm;

import com.ccp.dev.core.basic.base.BaseService;
import com.ccp.dev.core.basic.engine.GroovyScriptEngine;
import com.ccp.dev.core.basic.util.UUIDUtils;
import com.ccp.dev.core.util.BeanUtils;
import com.ccp.dev.core.util.StringPool;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.workflow.dao.BpmNodePrivilegeDao;
import com.ccp.dev.workflow.dao.BpmNodeSignDao;
import com.ccp.dev.workflow.model.bpm.BpmNodePrivilege;
import com.ccp.dev.workflow.model.bpm.BpmNodeSign;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * ClassDescribe:
 * 会签任务投票规则
 * @author :wangcheng
 * Date: 2019-09-05
 */
@Service
public class BpmNodeSignService extends BaseService<BpmNodeSign> {
    @Resource
    private BpmNodeSignDao bpmNodeSignDao;
    @Resource
    private BpmNodePrivilegeDao bpmNodePrivilegeDao;
    @Resource
    private GroovyScriptEngine groovyScriptEngine;

    /**
     * 根据发布id和节点id获取会签设置对象。
     * @param processDefinitionId 流程定义id
     * @param nodeId 节点id
     * @return
     */
    public BpmNodeSign getByProcDefIdAndNodeId(String processDefinitionId, String nodeId) {
        return bpmNodeSignDao.getByProcDefIdAndNodeId(processDefinitionId, nodeId);
    }

    /**
     * 判断当前用户(或组织,角色等)是否满足特权要求
     * @param procDefId 流程定义id
     * @param nodeId 节点id
     * @param type 特权类型
     * @param currentUserId 当前用户id
     * @param orgId 组织id
     */
    public boolean checkNodeSignPrivilege(String procDefId, String nodeId, BpmNodePrivilegeType type, String currentUserId, String orgId) {
        List<BpmNodePrivilege> privilegeList =  bpmNodePrivilegeDao.queryByProcDefIdAndNodeIdAndMode(procDefId, nodeId, type.getMode());

        // 没有配置特权
        if (BeanUtils.isEmpty(privilegeList)) {
            return false;
        }

        // 逐条校验特权规则
        for (BpmNodePrivilege rule : privilegeList) {
            int userType = rule.getUserType().intValue();
            switch (userType) {
                // 指定用户
                case NodeSignPrivilegeRuleChecker.ASSIGN_TYPE_USER:
                    if (StringUtil.isNotEmpty(rule.getCmpIds())) {
                        List<String> allowList = Arrays.asList(rule.getCmpIds().split(StringPool.COMMA));
                        boolean rtn = containList(allowList, currentUserId);
                        if (rtn) {
                            return true;
                        }
                    }
                // 指定组织
                case NodeSignPrivilegeRuleChecker.ASSIGN_TYPE_ORG:
                    if (!StringUtil.isEmpty(rule.getCmpIds())) {
                        List<String> allowList = Arrays.asList(rule.getCmpIds().split(StringPool.COMMA));
                        boolean rtn= containList(allowList,orgId);
                        if(rtn){
                            return true;
                        }
                    }
                // 根据脚本获取用户
                case NodeSignPrivilegeRuleChecker.ASSIGN_TYPE_SCRIPT:
                    Map<String, Object> vars = new HashMap<>();
                    Object result = groovyScriptEngine.executeObject(rule.getCmpNames(), vars);
                    if (result != null && result instanceof Set) {
                        Set<String> set = (Set<String>) result;
                        if (set.contains(currentUserId)) {
                            return true;
                        }
                    }
                default:
                    break;

            }

        }
        return false;
    }

    /**
     * 判断某数组中是否含有某个元素
     * @param list
     * @param id
     * @return
     */
    private boolean containList(List<String> list,String id){
        for(String str:list){
            if(str.equals(id)){
                return true;
            }
        }
        return false;
    }

    /**
     * 保存会签配置
     * @param bpmNodeSign 会签配置对象
     * @param privileges 特权对象
     */
    public void addOrUpdateSignAndPrivilege(BpmNodeSign bpmNodeSign, List<BpmNodePrivilege> privileges) {
        // 先处理BPM_NODE_SIGN表
        BpmNodeSign nodeSign = bpmNodeSignDao.getByProcDefIdAndNodeId(bpmNodeSign.getProcDefId(), bpmNodeSign.getNodeId());
        if (BeanUtils.isEmpty(nodeSign)) {
            bpmNodeSign.setSignId(UUIDUtils.getUUIDFor32());
            bpmNodeSignDao.add(bpmNodeSign);
        }else {
            bpmNodeSignDao.update(bpmNodeSign);
        }

        // 处理BPM_NODE_PRIVILEGE表
        // 先删除原有数据
        bpmNodePrivilegeDao.delByProcDefIdAndNodeId(bpmNodeSign.getProcDefId(), bpmNodeSign.getNodeId());

        // 添加本次提交的
        for (BpmNodePrivilege privilege : privileges) {
            privilege.setPrivilegeId(UUIDUtils.getUUIDFor32());
            privilege.setProcDefId(bpmNodeSign.getProcDefId());
            privilege.setNodeId(bpmNodeSign.getNodeId());
            bpmNodePrivilegeDao.add(privilege);
        }
    }

    /**
     * 根据流程定义id 和节点id 查询节点特权
     * @param procDefId 流程定义id
     * @param nodeId 节点id
     * @return
     */
    public List<BpmNodePrivilege> queryPrivilegesByProcDefIdAndNodeId(String procDefId, String nodeId) {
        return bpmNodePrivilegeDao.queryByProcDefIdAndNodeId(procDefId, nodeId);
    }

    /**
     * 会签节点特权规则
     */
    private static class NodeSignPrivilegeRuleChecker {
        public NodeSignPrivilegeRuleChecker() {}

        /**
         * 用户=1
         */
        public static final short ASSIGN_TYPE_USER = 1;
        /**
         * 组织=3
         */
        public static final short ASSIGN_TYPE_ORG = 3;
        /**
         * 组织负责人=4
         */
        public static final short ASSIGN_TYPE_ORG_CHARGE = 4;
        /**
         * 脚本
         */
        public static final short ASSIGN_TYPE_SCRIPT = 12;
    }

    /**
     * 节点特权类型
     */
    public enum BpmNodePrivilegeType{
        /**
         * 0:"拥有所有特权"
         */
        DEFAULT(0L),

        /**
         * 1:"允许直接处理"
         */
        ALL_DIRECT(1L),

        /**
         * 2:"允许一票制"
         */
        ALLOW_ONE_VOTE(2L),

        /**
         * 3:"允许补签"
         */
        ALLOW_RETROACTIVE(3L);

        private Long mode;

        BpmNodePrivilegeType(Long mode) {
            this.mode = mode;
        }

        /**
         * 获取特权模式
         * @return
         */
        public Long getMode() {
            return mode;
        }
    }
}
